package com.hup.utils.commons.hardware;

import lombok.Getter;
import lombok.Setter;
import lombok.extern.log4j.Log4j2;

import java.awt.*;
import java.awt.event.InputEvent;

/**
 * @author hugan
 * @date 2020/4/16
 */
@Log4j2
public class MouseController {

    /**
     * 实现控制鼠标
     */
    private Robot robot;
    /**
     * 控制鼠标时的[通行回调]
     */
    @Setter
    @Getter
    private McPermitCallback permitCallback;
    /**
     * 每次移动前,先触发一次[释放左键]的鼠标事件
     * 默认false;(实现用户拖拽时,键盘微调)
     * 另外某些场景时可以设为true
     */
    @Setter
    private boolean releaseLeftBeforeMove;

    /**
     * @param permitCallback nullable
     */
    public MouseController(McPermitCallback permitCallback) throws AWTException {
        robot = new Robot();
        this.permitCallback = permitCallback;
    }

    private boolean canAct() {
        return permitCallback == null || permitCallback.canAct();
    }

    private boolean canClick() {
        return permitCallback == null || permitCallback.canClick();
    }

    /**
     * 全局获取鼠标位置
     */
    public Point getLocation() {
        return MouseInfo.getPointerInfo().getLocation();
    }

    /**
     * 将鼠标移动到指定位置
     * @param x 范围[0,宽/高-1],传入数值在范围外时,总会返回false
     * @param y 范围[0,宽/高-1]
     * @return 成功移动到指定位置(代码会检查结果)
     */
    public boolean move(int x, int y) {
        if (!canAct()) return false;
        log.debug("moveTo.st:{},{}", x, y);
        if (releaseLeftBeforeMove) {
            robot.mouseRelease(InputEvent.BUTTON1_MASK);//移动前,不管有无按下都释放一次左键
        }

        HRobotJni.mouseMove(x, y);
        Point curLocation = getLocation();
        log.debug("moveTo.ed:{}", curLocation);
        if (curLocation.x != x || curLocation.y != y) {
            /*
            目前已知的两种情况:
            .用户移动了鼠标(要用户非常快速移动时,才会造成不一致)
            .jdk的bug,在缩放比例不等于100时就会有bug(已通过JniMouseUtil修复了)
             */
            log.warn("移动错误,目标={},{}, 结果={},{}", x, y, curLocation.x, curLocation.y);
            return false;
        }
        return true;
    }

    /**
     * 将鼠标移动diff个像素
     */
    public boolean moveDiff(int xDiff, int yDiff) {
        Point location = getLocation();
        log.debug("location={}", location);
        int x = location.x + xDiff;
        int y = location.y + yDiff;
        Dimension screenSize = ScreenUtil.getScreenSize();
        if (x < 0) x = 0;
        else if (x > screenSize.width - 1) x = screenSize.width - 1;
        if (y < 0) y = 0;
        else if (y > screenSize.height - 1) y = screenSize.height - 1;

        return move(x, y);
    }

    /**
     * 点击指定位置[先移动,后点击]
     */
    public boolean click(int x, int y) {
        if (!move(x, y)) return false;

        if (!canClick()) return false;
        robot.mousePress(InputEvent.BUTTON1_MASK);
        robot.delay(100);//简单测试结果,实体设备间隔:触控板[112,120],鼠标[75,90]
        robot.mouseRelease(InputEvent.BUTTON1_MASK);
        return true;
    }

    /**
     * 点击后 再移动diff个距离(用于检测是否有漏操作)
     */
    public boolean clickAndFlyY(int x, int y, int diffY) {
        if (!click(x, y)) return false;
        HRobotJni.mouseMove(x, y + diffY);
        return true;
    }

    /**
     * 点击后 再移动diff个距离(用于检测是否有漏操作)
     */
    public boolean clickAndFlyX(int x, int y, int diffX) {
        if (!click(x, y)) return false;
        HRobotJni.mouseMove(x + diffX, y);
        return true;
    }


    public interface McPermitCallback {
        /**
         * @return 能否操作(移动,点击)
         */
        boolean canAct();

        /**
         * 可以操作(可以移动)的情况下
         * @return 能否点击
         */
        boolean canClick();
    }

}
